home *** CD-ROM | disk | FTP | other *** search
/ System Booster / System Booster.iso / Archives / GNU / GNUPLOTsrc.lha / util3d.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-01-22  |  31.8 KB  |  1,280 lines

  1. #ifndef lint
  2. static char *RCSid = "$Id: util3d.c,v 1.8 1995/12/11 23:14:08 drd Exp $";
  3. #endif
  4.  
  5.  
  6. /* GNUPLOT - util3d.c */
  7. /*
  8.  * Copyright (C) 1986 - 1993   Thomas Williams, Colin Kelley
  9.  *
  10.  * Permission to use, copy, and distribute this software and its
  11.  * documentation for any purpose with or without fee is hereby granted, 
  12.  * provided that the above copyright notice appear in all copies and 
  13.  * that both that copyright notice and this permission notice appear 
  14.  * in supporting documentation.
  15.  *
  16.  * Permission to modify the software is granted, but not the right to
  17.  * distribute the modified code.  Modifications are to be distributed 
  18.  * as patches to released version.
  19.  *
  20.  * This software is provided "as is" without express or implied warranty.
  21.  *
  22.  *
  23.  * AUTHORS
  24.  *
  25.  *   Original Software:
  26.  *       Gershon Elber and many others.
  27.  *
  28.  * 19 September 1992  Lawrence Crowl  (crowl@cs.orst.edu)
  29.  * Added user-specified bases for log scaling.
  30.  *
  31.  * 3.6 - split graph3d.c into graph3d.c (graph),
  32.  *                            util3d.c (intersections, etc)
  33.  *                            hidden3d.c (hidden-line removal code)
  34.  *
  35.  * There is a mailing list for gnuplot users. Note, however, that the
  36.  * newsgroup 
  37.  *    comp.graphics.gnuplot 
  38.  * is identical to the mailing list (they
  39.  * both carry the same set of messages). We prefer that you read the
  40.  * messages through that newsgroup, to subscribing to the mailing list.
  41.  * (If you can read that newsgroup, and are already on the mailing list,
  42.  * please send a message info-gnuplot-request@dartmouth.edu, asking to be
  43.  * removed from the mailing list.)
  44.  *
  45.  * The address for mailing to list members is
  46.  *       info-gnuplot@dartmouth.edu
  47.  * and for mailing administrative requests is 
  48.  *       info-gnuplot-request@dartmouth.edu
  49.  * The mailing list for bug reports is 
  50.  *       bug-gnuplot@dartmouth.edu
  51.  * The list of those interested in beta-test versions is
  52.  *       info-gnuplot-beta@dartmouth.edu
  53.  */
  54.  
  55. #include <math.h>
  56. #if !defined(sequent) && !defined(apollo) && !defined(alliant)
  57. #include <limits.h>
  58. #endif
  59. #include "plot.h"
  60. #include "setshow.h"
  61.  
  62. extern int xleft,xright,ybot,ytop;
  63. extern int hidden_active, hidden_no_update;
  64.  
  65. /* ACCESS THINGS THAT OUGHT TO BE HIDDEN IN hidden3d.c - perhaps we
  66.  * can move the relevant code into hidden3d.c sometime
  67.  */
  68.  
  69. extern long int xmin_hl,xmax_hl;
  70. /* These arrays are used to keep track of the minimum and maximum y values used
  71.    for each X value.  These are only used for drawing the individual boxes that
  72.    make up the 3d figure.  After each box is drawn, the information is copied
  73.    to the bitmap. */
  74. extern short int *ymin_hl, *ymax_hl;
  75. extern int xfact, yfact;
  76. #define XREDUCE(X) ((X)/xfact)
  77. #define YREDUCE(Y) ((Y)/yfact)
  78. /* Bitmap of the screen.  The array for each x value is malloc-ed as needed */
  79. extern short int **pnt;
  80. #define IFSET(X,Y) (pnt[X] == 0 ? 0 : (((pnt[X])[(Y)>>4] >> ((Y) & 0xf)) & 0x01))
  81.  
  82. extern int suppressMove;
  83.  
  84.  
  85. extern double min_array[], max_array[];
  86. extern int auto_array[], log_array[];
  87. extern double base_array[], log_base_array[];
  88.  
  89. /* for convenience while converting to use these arrays */
  90. #define x_min3d min_array[FIRST_X_AXIS]
  91. #define x_max3d max_array[FIRST_X_AXIS]
  92. #define y_min3d min_array[FIRST_Y_AXIS]
  93. #define y_max3d max_array[FIRST_Y_AXIS]
  94. #define z_min3d min_array[FIRST_Z_AXIS]
  95. #define z_max3d max_array[FIRST_Z_AXIS]
  96. #define min3d_z min_array[FIRST_Z_AXIS]
  97. #define max3d_z max_array[FIRST_Z_AXIS]
  98.  
  99. /* both min() and MIN() are predefined by some compilers :-( */
  100.  
  101. #define GPMIN(a,b) ( (a) < (b) ? (a) : (b) )
  102. #define GPMAX(a,b) ( (a) > (b) ? (a) : (b) )
  103.  
  104. #define inrange(z,min,max) ((min<max) ? ((z>=min)&&(z<=max)) : ((z>=max)&&(z<=min)) )
  105.  
  106.  
  107. typedef double transform_matrix[4][4];
  108.  
  109. void mat_unit(mat)
  110. transform_matrix mat;
  111. {
  112.     int i, j;
  113.  
  114.     for (i = 0; i < 4; i++) for (j = 0; j < 4; j++)
  115.     if (i == j)
  116.         mat[i][j] = 1.0;
  117.     else
  118.         mat[i][j] = 0.0;
  119. }
  120.  
  121. void mat_trans(tx, ty, tz, mat)
  122. double tx, ty, tz;
  123. transform_matrix mat;
  124. {
  125.      mat_unit(mat);                                 /* Make it unit matrix. */
  126.      mat[3][0] = tx;
  127.      mat[3][1] = ty;
  128.      mat[3][2] = tz;
  129. }
  130.  
  131. void mat_scale(sx, sy, sz, mat)
  132. double sx, sy, sz;
  133. transform_matrix mat;
  134. {
  135.      mat_unit(mat);                                 /* Make it unit matrix. */
  136.      mat[0][0] = sx;
  137.      mat[1][1] = sy;
  138.      mat[2][2] = sz;
  139. }
  140.  
  141. void mat_rot_x(teta, mat)
  142. double teta;
  143. transform_matrix mat;
  144. {
  145.     double cos_teta, sin_teta;
  146.  
  147.     teta *= Pi / 180.0;
  148.     cos_teta = cos(teta);
  149.     sin_teta = sin(teta);
  150.  
  151.     mat_unit(mat);                                  /* Make it unit matrix. */
  152.     mat[1][1] = cos_teta;
  153.     mat[1][2] = -sin_teta;
  154.     mat[2][1] = sin_teta;
  155.     mat[2][2] = cos_teta;
  156. }
  157.  
  158. void mat_rot_y(teta, mat)
  159. double teta;
  160. transform_matrix mat;
  161. {
  162.     double cos_teta, sin_teta;
  163.  
  164.     teta *= Pi / 180.0;
  165.     cos_teta = cos(teta);
  166.     sin_teta = sin(teta);
  167.  
  168.     mat_unit(mat);                                  /* Make it unit matrix. */
  169.     mat[0][0] = cos_teta;
  170.     mat[0][2] = -sin_teta;
  171.     mat[2][0] = sin_teta;
  172.     mat[2][2] = cos_teta;
  173. }
  174.  
  175. void mat_rot_z(teta, mat)
  176. double teta;
  177. transform_matrix mat;
  178. {
  179.     double cos_teta, sin_teta;
  180.  
  181.     teta *= Pi / 180.0;
  182.     cos_teta = cos(teta);
  183.     sin_teta = sin(teta);
  184.  
  185.     mat_unit(mat);                                  /* Make it unit matrix. */
  186.     mat[0][0] = cos_teta;
  187.     mat[0][1] = -sin_teta;
  188.     mat[1][0] = sin_teta;
  189.     mat[1][1] = cos_teta;
  190. }
  191.  
  192. /* Multiply two transform_matrix. Result can be one of two operands. */
  193. void mat_mult(mat_res, mat1, mat2)
  194. transform_matrix mat_res, mat1, mat2;
  195. {
  196.     int i, j, k;
  197.     transform_matrix mat_res_temp;
  198.  
  199.     for (i = 0; i < 4; i++) for (j = 0; j < 4; j++) {
  200.         mat_res_temp[i][j] = 0;
  201.         for (k = 0; k < 4; k++) mat_res_temp[i][j] += mat1[i][k] * mat2[k][j];
  202.     }
  203.     for (i = 0; i < 4; i++) for (j = 0; j < 4; j++)
  204.     mat_res[i][j] = mat_res_temp[i][j];
  205. }
  206.  
  207.  
  208. /* Test a single point to be within the xleft,xright,ybot,ytop bbox.
  209.  * Sets the returned integers 4 l.s.b. as follows:
  210.  * bit 0 if to the left of xleft.
  211.  * bit 1 if to the right of xright.
  212.  * bit 2 if above of ytop.
  213.  * bit 3 if below of ybot.
  214.  * 0 is returned if inside.
  215.  */
  216. int clip_point(x, y)
  217. unsigned int x, y;
  218. {
  219.     int ret_val = 0;
  220.  
  221.     if (x < xleft) ret_val |= 0x01;
  222.     if (x > xright) ret_val |= 0x02;
  223.     if (y < ybot) ret_val |= 0x04;
  224.     if (y > ytop) ret_val |= 0x08;
  225.  
  226.     return ret_val;
  227. }
  228.  
  229. /* Clip the given line to drawing coords defined as xleft,xright,ybot,ytop.
  230.  *   This routine uses the cohen & sutherland bit mapping for fast clipping -
  231.  * see "Principles of Interactive Computer Graphics" Newman & Sproull page 65.
  232.  */
  233. void draw_clip_line(x1, y1, x2, y2)
  234. unsigned int x1, y1, x2, y2;
  235. {
  236.     int x, y, dx, dy, x_intr[4], y_intr[4], count, pos1, pos2;
  237.     register struct termentry *t = term;
  238.  
  239. #if defined(ATARI) || defined(MTOS)
  240.     if(x1<0||x2<0||y1<0||y2<0) return; /* temp bug fix */
  241. #endif
  242.  
  243.     pos1 = clip_point(x1, y1);
  244.     pos2 = clip_point(x2, y2);
  245.     if (pos1 || pos2) {
  246.     if (pos1 & pos2) return;         /* segment is totally out. */
  247.  
  248.     /* Here part of the segment MAY be inside. test the intersection
  249.      * of this segment with the 4 boundaries for hopefully 2 intersections
  250.      * in. If none are found segment is totaly out.
  251.      * Under rare circumstances there may be up to 4 intersections (e.g.
  252.      * when the line passes directly through at least one corner). In
  253.      * this case it is sufficient to take any 2 intersections (e.g. the
  254.      * first two found).
  255.      */
  256.     count = 0;
  257.     dx = x2 - x1;
  258.     dy = y2 - y1;
  259.  
  260.     /* Find intersections with the x parallel bbox lines: */
  261.     if (dy != 0) {
  262.         x = (ybot - y2) * dx / dy + x2;      /* Test for ybot boundary. */
  263.         if (x >= xleft && x <= xright) {
  264.         x_intr[count] = x;
  265.         y_intr[count++] = ybot;
  266.         }
  267.         x = (ytop - y2) * dx / dy + x2;      /* Test for ytop boundary. */
  268.         if (x >= xleft && x <= xright) {
  269.         x_intr[count] = x;
  270.         y_intr[count++] = ytop;
  271.         }
  272.     }
  273.  
  274.     /* Find intersections with the y parallel bbox lines: */
  275.     if (dx != 0) {
  276.         y = (xleft - x2) * dy / dx + y2;    /* Test for xleft boundary. */
  277.         if (y >= ybot && y <= ytop) {
  278.         x_intr[count] = xleft;
  279.         y_intr[count++] = y;
  280.         }
  281.         y = (xright - x2) * dy / dx + y2;  /* Test for xright boundary. */
  282.         if (y >= ybot && y <= ytop) {
  283.         x_intr[count] = xright;
  284.         y_intr[count++] = y;
  285.         }
  286.     }
  287.  
  288.     if (count >= 2) {
  289.         int x_max, x_min, y_max, y_min;
  290.  
  291.         x_min = GPMIN(x1, x2);
  292.         x_max = GPMAX(x1, x2);
  293.         y_min = GPMIN(y1, y2);
  294.         y_max = GPMAX(y1, y2);
  295.  
  296.         if (pos1 && pos2) {             /* Both were out - update both */
  297.         x1 = x_intr[0];
  298.         y1 = y_intr[0];
  299.         x2 = x_intr[1];
  300.         y2 = y_intr[1];
  301.         }
  302.         else if (pos1) {         /* Only x1/y1 was out - update only it */
  303.         if (dx * (x2 - x_intr[0]) + dy * (y2 - y_intr[0]) > 0) {
  304.             x1 = x_intr[0];
  305.             y1 = y_intr[0];
  306.         }
  307.         else {
  308.             x1 = x_intr[1];
  309.             y1 = y_intr[1];
  310.         }
  311.         }
  312.         else {                    /* Only x2/y2 was out - update only it */
  313.         if (dx * (x_intr[0] - x1) + dy * (y_intr[0] - y1) > 0) {
  314.             x2 = x_intr[0];
  315.             y2 = y_intr[0];
  316.         }
  317.         else {
  318.             x2 = x_intr[1];
  319.             y2 = y_intr[1];
  320.         }
  321.         }
  322.  
  323.         if (x1 < x_min || x1 > x_max ||
  324.         x2 < x_min || x2 > x_max ||
  325.         y1 < y_min || y1 > y_max ||
  326.         y2 < y_min || y2 > y_max) return;
  327.     }
  328.     else
  329.         return;
  330.     }
  331.  
  332. /* I'D REALLY LIKE TO MOVE THIS INTO hidden3d.c AS A FN - it shouldn't
  333.  * be accessing secret variables like this
  334.  */
  335.     
  336. #ifndef LITE
  337.     if(hidden3d && hidden_active && draw_surface)
  338.       {
  339.     char flag;
  340.     register int xv, yv, errx, erry, err;
  341.     register int xvr, yvr;
  342.     int xve, yve;
  343.     register int dy, nstep=0, dyr;
  344.     int i;
  345.     if (x1 > x2){
  346.       xvr = x2;
  347.       yvr = y2;
  348.       xve = x1;
  349.       yve = y1;
  350.     } else {
  351.       xvr = x1;
  352.       yvr = y1;
  353.       xve = x2;
  354.       yve = y2;
  355.     };
  356.     errx = XREDUCE(xve) - XREDUCE(xvr);
  357.     erry = YREDUCE(yve) - YREDUCE(yvr);
  358.     dy = (erry > 0 ? 1 : -1);
  359.     dyr = dy*yfact;
  360.     switch (dy){
  361.     case 1:
  362.       nstep = errx + erry;
  363.       errx = -errx;
  364.       break;
  365.     case -1:
  366.       nstep = errx - erry;
  367.       errx = -errx;
  368.       erry = -erry;
  369.       break;
  370.     };
  371.     err = errx + erry;
  372.     errx <<= 1;
  373.     erry <<= 1;
  374.     xv = XREDUCE(xvr) - XREDUCE(xleft);
  375.     yv = YREDUCE(yvr) - YREDUCE(ybot);
  376.     (*t->move)(xvr,yvr);
  377.     if( !IFSET(xv,yv) ) flag = 0;
  378.     else flag = 1;
  379.     if(!hidden_no_update){ /* Check first point */
  380.       if (xv < xmin_hl) xmin_hl = xv;
  381.       if (xv > xmax_hl) xmax_hl = xv;
  382.       if (yv > ymax_hl[xv]) ymax_hl[xv] = yv;
  383.       if (yv < ymin_hl[xv]) ymin_hl[xv] = yv;
  384.     };
  385.     for (i=0;i<nstep;i++){
  386.       if (err < 0){
  387.         xv ++;
  388.         xvr += xfact;
  389.         err += erry;
  390.       } else {
  391.         yv += dy;
  392.         yvr += dyr;
  393.         err += errx;
  394.       };
  395.       if( !IFSET(xv,yv)){
  396.         if(flag != 0) {(*t->move)(xvr,yvr); flag = 0;};
  397.       } else {
  398.         if(flag == 0) {(*t->vector)(xvr,yvr); flag = 1;};
  399.       };
  400.       if(!hidden_no_update){
  401.         if (xv < xmin_hl) xmin_hl = xv;
  402.         if (xv > xmax_hl) xmax_hl = xv;
  403.         if (yv > ymax_hl[xv]) ymax_hl[xv] = yv;
  404.         if (yv < ymin_hl[xv]) ymin_hl[xv] = yv;
  405.       };
  406.     };
  407.     if (flag == 0) (*t->vector)(xve, yve);
  408.     return;
  409.       };
  410. #endif /* not LITE */
  411.     if(!suppressMove) (*t->move)(x1,y1);
  412.     (*t->vector)(x2,y2);
  413. }
  414.  
  415. /* Two routine to emulate move/vector sequence using line drawing routine. */
  416. static unsigned int move_pos_x, move_pos_y;
  417.  
  418. void clip_move(x,y)
  419. unsigned int x,y;
  420. {
  421.     move_pos_x = x;
  422.     move_pos_y = y;
  423. }
  424.  
  425. void clip_vector(x,y)
  426. unsigned int x,y;
  427. {
  428.     draw_clip_line(move_pos_x,move_pos_y, x, y);
  429.     move_pos_x = x;
  430.     move_pos_y = y;
  431. }
  432.  
  433. /* And text clipping routine. */
  434. void clip_put_text(x, y, str)
  435. unsigned int x,y;
  436. char *str;
  437. {
  438.     register struct termentry *t = term;
  439.  
  440.     if (clip_point(x, y)) return;
  441.  
  442.     (*t->put_text)(x,y,str);
  443. }
  444.  
  445. /* seems sensible to put the justification in here too..? */
  446. void clip_put_text_just(x,y,str,just)
  447. unsigned int x,y;
  448. char *str;
  449. int just;
  450. {
  451.     register struct termentry *t = term;
  452.     if (clip_point(x,y)) return;
  453.     if (!(*t->justify_text)(just)) {
  454.         assert(CENTRE==1 && RIGHT==2);
  455.         x -= (t->h_char * strlen(str) * just)/2;
  456.     }
  457.     (*t->put_text)(x,y,str);
  458. }
  459.  
  460. /* single edge intersection algorithm */
  461. /* Given two points, one inside and one outside the plot, return
  462.  * the point where an edge of the plot intersects the line segment defined 
  463.  * by the two points.
  464.  */
  465. void edge3d_intersect(points, i, ex, ey, ez)
  466.     struct coordinate GPHUGE *points; /* the points array */
  467.     int i;                /* line segment from point i-1 to point i */
  468.     double *ex, *ey, *ez;        /* the point where it crosses an edge */
  469. {
  470.     /* global x_min3d, x_max3d, y_min3d, y_max3d, min3d_z, max3d_z */
  471.     int count;
  472.     double ix = points[i-1].x;
  473.     double iy = points[i-1].y;
  474.     double iz = points[i-1].z;
  475.     double ox = points[i].x;
  476.     double oy = points[i].y;
  477.     double oz = points[i].z;
  478.     double x, y, z;            /* possible intersection point */
  479.  
  480.     if(points[i].type == INRANGE)
  481.       {
  482.     /* swap points around so that ix/ix/iz are INRANGE and ox/oy/oz are OUTRANGE */
  483.     x = ix;ix = ox;ox = x;
  484.     y = iy;iy = oy;oy = y;
  485.     z = iz;iz = oz;oz = z;
  486.       }
  487.  
  488.     /* nasty degenerate cases, effectively drawing to an infinity point (?)
  489.        cope with them here, so don't process them as a "real" OUTRANGE point 
  490.  
  491.        If more than one coord is -VERYLARGE, then can't ratio the "infinities"
  492.        so drop out by returning the INRANGE point.
  493.  
  494.        Obviously, only need to test the OUTRANGE point (coordinates) */
  495.  
  496.     /* nasty degenerate cases, effectively drawing to an infinity point (?)
  497.        cope with them here, so don't process them as a "real" OUTRANGE point 
  498.  
  499.        If more than one coord is -VERYLARGE, then can't ratio the "infinities"
  500.        so drop out by returning FALSE */
  501.     
  502.     count = 0;
  503.     if(ox == -VERYLARGE) count++;
  504.     if(oy == -VERYLARGE) count++;
  505.     if(oz == -VERYLARGE) count++;
  506.  
  507.     /* either doesn't pass through 3D volume *or* 
  508.        can't ratio infinities to get a direction to draw line, so return the INRANGE point */
  509.     if(count > 1){
  510.       *ex = ix;
  511.       *ey = iy;
  512.       *ez = iz;
  513.  
  514.       return;
  515.     }
  516.  
  517.     if(count == 1)
  518.       {
  519.     *ex = ix;
  520.     *ey = iy;
  521.     *ez = iz;
  522.  
  523.     if(ox == -VERYLARGE)
  524.       {
  525.         *ex = x_min3d;
  526.         return;
  527.       }
  528.  
  529.     if(oy == -VERYLARGE)
  530.       {
  531.         *ey = y_min3d;
  532.         return;
  533.       }
  534.  
  535.     /* obviously oz is -VERYLARGE and (ox != -VERYLARGE && oy != -VERYLARGE) */
  536.     *ez = min3d_z;
  537.     return;
  538.       }
  539.  
  540.     /*
  541.      * Can't have case (ix == ox && iy == oy && iz == oz) as one point
  542.      * is INRANGE and one point is OUTRANGE.
  543.      */
  544.     if(ix == ox) {
  545.       if(iy == oy) {
  546.     /* line parallel to z axis */
  547.  
  548.     /* assume inrange(iy, y_min3d, y_max3d) && inrange(ix, x_min3d, x_max3d) */
  549.     *ex = ix;        /* == ox */
  550.     *ey = iy;        /* == oy */
  551.     
  552.     if (inrange(max3d_z, iz, oz))
  553.       *ez = max3d_z;
  554.     else if (inrange(min3d_z, iz, oz))
  555.       *ez = min3d_z;
  556.     else {
  557.       graph_error("error in edge3d_intersect");
  558.     }
  559.  
  560.     return;
  561.       }
  562.       
  563.       if(iz == oz) {
  564.     /* line parallel to y axis */
  565.     
  566.     /* assume inrange(iz, min3d_z, max3d_z) && inrange(ix, x_min3d, x_max3d) */
  567.     *ex = ix;        /* == ox */
  568.     *ez = iz;        /* == oz */
  569.     
  570.     if (inrange(y_max3d, iy, oy))
  571.       *ey = y_max3d;
  572.     else if (inrange(y_min3d, iy, oy))
  573.       *ey = y_min3d;
  574.     else {
  575.       graph_error("error in edge3d_intersect");
  576.     }
  577.     
  578.     return;
  579.       }
  580.  
  581.       /* nasty 2D slanted line in a yz plane */
  582.       
  583.       /* does it intersect y_min3d edge */
  584.       if (inrange(y_min3d, iy, oy) && y_min3d != iy && y_min3d != oy) {
  585.     z = iz + (y_min3d-iy) * ((oz-iz) / (oy-iy));
  586.     if (inrange(z, min3d_z, max3d_z)) {
  587.       *ex = ix;
  588.       *ey = y_min3d;
  589.       *ez = z;
  590.       return;
  591.     }
  592.       }
  593.  
  594.       /* does it intersect y_max3d edge */
  595.       if (inrange(y_max3d, iy, oy) && y_max3d != iy && y_max3d != oy) {
  596.     z = iz + (y_max3d-iy) * ((oz-iz) / (oy-iy));
  597.     if (inrange(z, min3d_z, max3d_z)) {
  598.       *ex = ix;
  599.       *ey = y_max3d;
  600.       *ez = z;
  601.       return;
  602.     }
  603.       }
  604.  
  605.       /* does it intersect min3d_z edge */
  606.       if (inrange(min3d_z, iz, oz) && min3d_z != iz && min3d_z != oz) {
  607.     y = iy + (min3d_z-iz) * ((oy-iy) / (oz-iz));
  608.     if (inrange(y, y_min3d, y_max3d)) {
  609.       *ex = ix;
  610.       *ey = y;
  611.       *ez = min3d_z;
  612.       return;
  613.     }
  614.       }
  615.  
  616.       /* does it intersect max3d_z edge */
  617.       if (inrange(max3d_z, iz, oz) && max3d_z != iz && max3d_z != oz) {
  618.     y = iy + (max3d_z-iz) * ((oy-iy) / (oz-iz));
  619.     if (inrange(y, y_min3d, y_max3d)) {
  620.       *ex = ix;
  621.       *ey = y;
  622.       *ez = max3d_z;
  623.       return;
  624.     }
  625.       }
  626.     }
  627.       
  628.     if(iy == oy) {
  629.       /* already checked case (ix == ox && iy == oy) */
  630.       if(oz ==  iz) {
  631.       /* line parallel to x axis */
  632.  
  633.       /* assume inrange(iz, min3d_z, max3d_z) && inrange(iy, y_min3d, y_max3d) */
  634.       *ey = iy;        /* == oy */
  635.       *ez = iz;        /* == oz */
  636.       
  637.       if (inrange(x_max3d, ix, ox))
  638.         *ex = x_max3d;
  639.       else if (inrange(x_min3d, ix, ox))
  640.         *ex = x_min3d;
  641.       else {
  642.         graph_error("error in edge3d_intersect");
  643.       }
  644.  
  645.       return;
  646.       }
  647.  
  648.       /* nasty 2D slanted line in an xz plane */
  649.  
  650.       /* does it intersect x_min3d edge */
  651.       if (inrange(x_min3d, ix, ox) && x_min3d != ix && x_min3d != ox) {
  652.     z = iz + (x_min3d-ix) * ((oz-iz) / (ox-ix));
  653.     if (inrange(z, min3d_z, max3d_z)) {
  654.       *ex = x_min3d;
  655.       *ey = iy;
  656.       *ez = z;
  657.       return;
  658.     }
  659.       }
  660.  
  661.       /* does it intersect x_max3d edge */
  662.       if (inrange(x_max3d, ix, ox) && x_max3d != ix && x_max3d != ox) {
  663.     z = iz + (x_max3d-ix) * ((oz-iz) / (ox-ix));
  664.     if (inrange(z, min3d_z, max3d_z)) {
  665.       *ex = x_max3d;
  666.       *ey = iy;
  667.       *ez = z;
  668.       return;
  669.     }
  670.       }
  671.  
  672.       /* does it intersect min3d_z edge */
  673.       if (inrange(min3d_z, iz, oz) && min3d_z != iz && min3d_z != oz) {
  674.     x = ix + (min3d_z-iz) * ((ox-ix) / (oz-iz));
  675.     if (inrange(x, x_min3d, x_max3d)) {
  676.       *ex = x;
  677.       *ey = iy;
  678.       *ez = min3d_z;
  679.       return;
  680.     }
  681.       }
  682.       
  683.       /* does it intersect max3d_z edge */
  684.       if (inrange(max3d_z, iz, oz) && max3d_z != iz && max3d_z != oz) {
  685.     x = ix + (max3d_z-iz) * ((ox-ix) / (oz-iz));
  686.     if (inrange(x, x_min3d, x_max3d)) {
  687.       *ex = x;
  688.       *ey = iy;
  689.       *ez = max3d_z;
  690.       return;
  691.     }
  692.       }      
  693.     }
  694.     
  695.     if(iz == oz) {
  696.       /* already checked cases (ix == ox && iz == oz) and (iy == oy && iz == oz) */
  697.  
  698.       /* nasty 2D slanted line in an xy plane */
  699.  
  700.       /* assume inrange(oz, min3d_z, max3d_z) */
  701.  
  702.       /* does it intersect x_min3d edge */
  703.       if (inrange(x_min3d, ix, ox) && x_min3d != ix && x_min3d != ox) {
  704.     y = iy + (x_min3d-ix) * ((oy-iy) / (ox-ix));
  705.     if (inrange(y, y_min3d, y_max3d)) {
  706.       *ex = x_min3d;
  707.       *ey = y;
  708.       *ez = iz;
  709.       return;
  710.     }
  711.       }
  712.  
  713.       /* does it intersect x_max3d edge */
  714.       if (inrange(x_max3d, ix, ox) && x_max3d != ix && x_max3d != ox) {
  715.     y = iy + (x_max3d-ix) * ((oy-iy) / (ox-ix));
  716.     if (inrange(y, y_min3d, y_max3d)) {
  717.       *ex = x_max3d;
  718.       *ey = y;
  719.       *ez = iz;
  720.       return;
  721.     }
  722.       }    
  723.       
  724.       /* does it intersect y_min3d edge */
  725.       if (inrange(y_min3d, iy, oy) && y_min3d != iy && y_min3d != oy) {
  726.     x = ix + (y_min3d-iy) * ((ox-ix) / (oy-iy));
  727.     if (inrange(x, x_min3d, x_max3d)) {
  728.       *ex = x;
  729.       *ey = y_min3d;
  730.       *ez = iz;
  731.       return;
  732.     }
  733.       }
  734.       
  735.       /* does it intersect y_max3d edge */
  736.       if (inrange(y_max3d, iy, oy) && y_max3d != iy && y_max3d != oy) {
  737.     x = ix + (y_max3d-iy) * ((ox-ix) / (oy-iy));
  738.     if (inrange(x, x_min3d, x_max3d)) {
  739.       *ex = x;
  740.       *ey = y_max3d;
  741.       *ez = iz;
  742.       return;
  743.     }
  744.       }
  745.     }
  746.   
  747.     /* really nasty general slanted 3D case */
  748.  
  749.     /* does it intersect x_min3d edge */
  750.     if (inrange(x_min3d, ix, ox) && x_min3d != ix && x_min3d != ox) {
  751.       y = iy + (x_min3d-ix) * ((oy-iy) / (ox-ix));
  752.       z = iz + (x_min3d-ix) * ((oz-iz) / (ox-ix));
  753.       if (inrange(y, y_min3d, y_max3d) && inrange(z, min3d_z, max3d_z)) {
  754.     *ex = x_min3d;
  755.     *ey = y;
  756.     *ez = z;
  757.     return;
  758.       }
  759.     }
  760.  
  761.     /* does it intersect x_max3d edge */
  762.     if (inrange(x_max3d, ix, ox) && x_max3d != ix && x_max3d != ox) {
  763.       y = iy + (x_max3d-ix) * ((oy-iy) / (ox-ix));
  764.       z = iz + (x_max3d-ix) * ((oz-iz) / (ox-ix));
  765.       if (inrange(y, y_min3d, y_max3d) && inrange(z, min3d_z, max3d_z)) {
  766.     *ex = x_max3d;
  767.     *ey = y;
  768.     *ez = z;
  769.     return;
  770.       }
  771.     }
  772.  
  773.     /* does it intersect y_min3d edge */
  774.     if (inrange(y_min3d, iy, oy) && y_min3d != iy && y_min3d != oy) {
  775.       x = ix + (y_min3d-iy) * ((ox-ix) / (oy-iy));
  776.       z = iz + (y_min3d-iy) * ((oz-iz) / (oy-iy));
  777.       if (inrange(x, x_min3d, x_max3d) && inrange(z, min3d_z, max3d_z)) {
  778.     *ex = x;
  779.     *ey = y_min3d;
  780.     *ez = z;
  781.     return;
  782.       }
  783.     }
  784.  
  785.     /* does it intersect y_max3d edge */
  786.     if (inrange(y_max3d, iy, oy) && y_max3d != iy && y_max3d != oy) {
  787.       x = ix + (y_max3d-iy) * ((ox-ix) / (oy-iy));
  788.       z = iz + (y_max3d-iy) * ((oz-iz) / (oy-iy));
  789.       if (inrange(x, x_min3d, x_max3d) && inrange(z, min3d_z, max3d_z)) {
  790.     *ex = x;
  791.     *ey = y_max3d;
  792.     *ez = z;
  793.     return;
  794.       }
  795.     }
  796.  
  797.     /* does it intersect min3d_z edge */
  798.     if (inrange(min3d_z, iz, oz) && min3d_z != iz && min3d_z != oz) {
  799.       x = ix + (min3d_z-iz) * ((ox-ix) / (oz-iz));
  800.       y = iy + (min3d_z-iz) * ((oy-iy) / (oz-iz));
  801.       if (inrange(x, x_min3d, x_max3d) && inrange(y, y_min3d, y_max3d)) {
  802.     *ex = x;
  803.     *ey = y;
  804.     *ez = min3d_z;
  805.     return;
  806.       }
  807.     }
  808.  
  809.     /* does it intersect max3d_z edge */
  810.     if (inrange(max3d_z, iz, oz) && max3d_z != iz && max3d_z != oz) {
  811.       x = ix + (max3d_z-iz) * ((ox-ix) / (oz-iz));
  812.       y = iy + (max3d_z-iz) * ((oy-iy) / (oz-iz));
  813.       if (inrange(x, x_min3d, x_max3d) && inrange(y, y_min3d, y_max3d)) {
  814.     *ex = x;
  815.     *ey = y;
  816.     *ez = max3d_z;
  817.     return;
  818.       }
  819.     }
  820.  
  821.     /* If we reach here, the inrange point is on the edge, and
  822.      * the line segment from the outrange point does not cross any 
  823.      * other edges to get there. In this case, we return the inrange 
  824.      * point as the 'edge' intersection point. This will basically draw
  825.      * line.
  826.      */
  827.     *ex = ix;
  828.     *ey = iy;
  829.     *ez = iz;
  830.     return;
  831. }
  832.  
  833. /* double edge intersection algorithm */
  834. /* Given two points, both outside the plot, return
  835.  * the points where an edge of the plot intersects the line segment defined 
  836.  * by the two points. There may be zero, one, two, or an infinite number
  837.  * of intersection points. (One means an intersection at a corner, infinite
  838.  * means overlaying the edge itself). We return FALSE when there is nothing
  839.  * to draw (zero intersections), and TRUE when there is something to 
  840.  * draw (the one-point case is a degenerate of the two-point case and we do 
  841.  * not distinguish it - we draw it anyway).
  842.  */
  843. TBOOLEAN                                /* any intersection? */
  844. two_edge3d_intersect(points, i, lx, ly, lz)
  845.     struct coordinate GPHUGE *points; /* the points array */
  846.     int i;                /* line segment from point i-1 to point i */
  847.     double *lx, *ly, *lz;        /* lx[2], ly[2], lz[2]: points where it crosses edges */
  848. {
  849.     int count;
  850.     /* global x_min3d, x_max3d, y_min3d, y_max3d, min3d_z, max3d_z */
  851.     double ix = points[i-1].x;
  852.     double iy = points[i-1].y;
  853.     double iz = points[i-1].z;
  854.     double ox = points[i].x;
  855.     double oy = points[i].y;
  856.     double oz = points[i].z;
  857.     double t[6];
  858.     double swap;
  859.     double x, y, z;            /* possible intersection point */
  860.     double t_min, t_max;
  861.  
  862.     /* nasty degenerate cases, effectively drawing to an infinity point (?)
  863.        cope with them here, so don't process them as a "real" OUTRANGE point 
  864.  
  865.        If more than one coord is -VERYLARGE, then can't ratio the "infinities"
  866.        so drop out by returning FALSE */
  867.     
  868.     count = 0;
  869.     if(ix == -VERYLARGE) count++;
  870.     if(ox == -VERYLARGE) count++;
  871.     if(iy == -VERYLARGE) count++;
  872.     if(oy == -VERYLARGE) count++;
  873.     if(iz == -VERYLARGE) count++;
  874.     if(oz == -VERYLARGE) count++;
  875.  
  876.     /* either doesn't pass through 3D volume *or* 
  877.        can't ratio infinities to get a direction to draw line, so simply return(FALSE) */
  878.     if(count > 1){
  879.       return(FALSE);
  880.     }
  881.  
  882.     if(ox == -VERYLARGE || ix == -VERYLARGE)
  883.       {
  884.     if(ix == -VERYLARGE)
  885.       {
  886.         /* swap points so ix/iy/iz don't have a -VERYLARGE component */
  887.         x = ix;ix = ox;ox = x;
  888.         y = iy;iy = oy;oy = y;
  889.         z = iz;iz = oz;oz = z;
  890.       }
  891.  
  892.     /* check actually passes through the 3D graph volume */
  893.     if(ix > x_max3d && inrange(iy, y_min3d, y_max3d) && inrange(iz, min3d_z, max3d_z))
  894.       {
  895.         lx[0] = x_min3d;
  896.         ly[0] = iy;
  897.         lz[0] = iz;
  898.  
  899.         lx[1] = x_max3d;
  900.         ly[1] = iy;
  901.         lz[1] = iz;
  902.  
  903.         return(TRUE);
  904.       }
  905.     else {
  906.       return(FALSE);
  907.     }
  908.       }
  909.  
  910.     if(oy == -VERYLARGE || iy == -VERYLARGE)
  911.       {
  912.     if(iy == -VERYLARGE)
  913.       {
  914.         /* swap points so ix/iy/iz don't have a -VERYLARGE component */
  915.         x = ix;ix = ox;ox = x;
  916.         y = iy;iy = oy;oy = y;
  917.         z = iz;iz = oz;oz = z;
  918.       }
  919.  
  920.     /* check actually passes through the 3D graph volume */
  921.     if(iy > y_max3d && inrange(ix, x_min3d, x_max3d) && inrange(iz, min3d_z, max3d_z))
  922.       {
  923.         lx[0] = ix;
  924.         ly[0] = y_min3d;
  925.         lz[0] = iz;
  926.  
  927.         lx[1] = ix;
  928.         ly[1] = y_max3d;
  929.         lz[1] = iz;
  930.  
  931.         return(TRUE);
  932.       }
  933.     else {
  934.       return(FALSE);
  935.     }
  936.       }
  937.  
  938.     if(oz == -VERYLARGE || iz == -VERYLARGE)
  939.       {
  940.     if(iz == -VERYLARGE)
  941.       {
  942.         /* swap points so ix/iy/iz don't have a -VERYLARGE component */
  943.         x = ix;ix = ox;ox = x;
  944.         y = iy;iy = oy;oy = y;
  945.         z = iz;iz = oz;oz = z;
  946.       }
  947.  
  948.     /* check actually passes through the 3D graph volume */
  949.     if(iz > max3d_z && inrange(ix, x_min3d, x_max3d) && inrange(iy, y_min3d, y_max3d))
  950.       {
  951.         lx[0] = ix;
  952.         ly[0] = iy;
  953.         lz[0] = min3d_z;
  954.  
  955.         lx[1] = ix;
  956.         ly[1] = iy;
  957.         lz[1] = max3d_z;
  958.  
  959.         return(TRUE);
  960.       }
  961.     else {
  962.       return(FALSE);
  963.     }
  964.       }
  965.  
  966.     /*
  967.      * Quick outcode tests on the 3d graph volume
  968.      */
  969.  
  970.     /* 
  971.      * test z coord first --- most surface OUTRANGE points generated between
  972.      * min3d_z and z_min3d (i.e. when ticslevel is non-zero)
  973.      */
  974.     if(GPMAX(iz,oz) < min3d_z || GPMIN(iz,oz) > max3d_z)
  975.       return(FALSE);
  976.  
  977.     if(GPMAX(ix,ox) < x_min3d || GPMIN(ix,ox) > x_max3d)
  978.       return(FALSE);
  979.  
  980.     if(GPMAX(iy,oy) < y_min3d || GPMIN(iy,oy) > y_max3d)
  981.       return(FALSE);
  982.  
  983.     /*
  984.      * Special horizontal/vertical, etc. cases are checked and remaining
  985.      * slant lines are checked separately.
  986.      *
  987.      * The slant line intersections are solved using the parametric form
  988.      * of the equation for a line, since if we test x/y/z min/max planes explicitly
  989.      * then e.g. a  line passing through a corner point (x_min,y_min,z_min) 
  990.      * actually intersects all 3 planes and hence further tests would be required 
  991.      * to anticipate this and similar situations.
  992.      */
  993.  
  994.     /*
  995.      * Can have case (ix == ox && iy == oy && iz == oz) as both points OUTRANGE
  996.      */
  997.     if(ix == ox && iy == oy && iz == oz)
  998.       {
  999.     /* but as only define single outrange point, can't intersect 3D graph volume */
  1000.     return(FALSE);
  1001.       }
  1002.  
  1003.     if(ix == ox) {
  1004.       if(iy == oy) {
  1005.     /* line parallel to z axis */
  1006.  
  1007.     /* x and y coords must be in range, and line must span both min3d_z and max3d_z */
  1008.     /* note that spanning min3d_z implies spanning max3d_z as both points OUTRANGE */
  1009.     if(!inrange(ix, x_min3d, x_max3d) || !inrange(iy, y_min3d, y_max3d))
  1010.       {
  1011.         return(FALSE);
  1012.       }
  1013.  
  1014.     if (inrange(min3d_z, iz, oz)) {
  1015.       lx[0] = ix;
  1016.       ly[0] = iy;
  1017.       lz[0] = min3d_z;
  1018.  
  1019.       lx[1] = ix;
  1020.       ly[1] = iy;
  1021.       lz[1] = max3d_z;
  1022.  
  1023.       return(TRUE);
  1024.     } else
  1025.       return(FALSE);
  1026.       }
  1027.       
  1028.       if(iz == oz) {
  1029.     /* line parallel to y axis */
  1030.     
  1031.     /* x and z coords must be in range, and line must span both y_min3d and y_max3d */
  1032.     /* note that spanning y_min3d implies spanning y_max3d, as both points OUTRANGE */
  1033.     if(!inrange(ix, x_min3d, x_max3d) || !inrange(iz, min3d_z, max3d_z))
  1034.       {
  1035.         return(FALSE);
  1036.       }
  1037.  
  1038.     if (inrange(y_min3d, iy, oy)) {
  1039.       lx[0] = ix;
  1040.       ly[0] = y_min3d;
  1041.       lz[0] = iz;
  1042.  
  1043.       lx[1] = ix;
  1044.       ly[1] = y_max3d;
  1045.       lz[1] = iz;
  1046.  
  1047.       return(TRUE);
  1048.     } else
  1049.       return(FALSE);
  1050.       }
  1051.  
  1052.       /* nasty 2D slanted line in a yz plane */
  1053.       
  1054.       if(!inrange(ox, x_min3d, x_max3d))
  1055.     return(FALSE);
  1056.  
  1057.       t[0] = (y_min3d - iy)/(oy - iy);
  1058.       t[1] = (y_max3d - iy)/(oy - iy);
  1059.       
  1060.       if(t[0] > t[1]) {
  1061.     swap = t[0];t[0] = t[1];t[1] = swap;
  1062.       }
  1063.  
  1064.       t[2] = (min3d_z - iz)/(oz - iz);
  1065.       t[3] = (max3d_z - iz)/(oz - iz);
  1066.       
  1067.       if(t[2] > t[3]) {
  1068.     swap = t[2];t[2] = t[3];t[3] = swap;
  1069.       }
  1070.  
  1071.       t_min = GPMAX(GPMAX(t[0],t[2]),0.0);
  1072.       t_max = GPMIN(GPMIN(t[1],t[3]),1.0);
  1073.       
  1074.       if(t_min > t_max)
  1075.     return(FALSE);
  1076.  
  1077.       lx[0] = ix;
  1078.       ly[0] = iy + t_min * (oy - iy);
  1079.       lz[0] = iz + t_min * (oz - iz);
  1080.       
  1081.       lx[1] = ix;
  1082.       ly[1] = iy + t_max * (oy - iy);
  1083.       lz[1] = iz + t_max * (oz - iz);
  1084.       
  1085.       /*
  1086.        * Can only have 0 or 2 intersection points -- only need test one coord
  1087.        */
  1088.       if(inrange(ly[0], y_min3d, y_max3d) && 
  1089.      inrange(lz[0], min3d_z, max3d_z))
  1090.     {
  1091.       return(TRUE);
  1092.     }
  1093.       
  1094.       return(FALSE);
  1095.     }
  1096.       
  1097.     if(iy == oy) {
  1098.       /* already checked case (ix == ox && iy == oy) */
  1099.       if(oz ==  iz) {
  1100.     /* line parallel to x axis */
  1101.     
  1102.     /* y and z coords must be in range, and line must span both x_min3d and x_max3d */
  1103.     /* note that spanning x_min3d implies spanning x_max3d, as both points OUTRANGE */
  1104.     if(!inrange(iy, y_min3d, y_max3d) || !inrange(iz, min3d_z, max3d_z))
  1105.       {
  1106.         return(FALSE);
  1107.       }
  1108.  
  1109.     if (inrange(x_min3d, ix, ox)) {
  1110.       lx[0] = x_min3d;
  1111.       ly[0] = iy;
  1112.       lz[0] = iz;
  1113.  
  1114.       lx[1] = x_max3d;
  1115.       ly[1] = iy;
  1116.       lz[1] = iz;
  1117.  
  1118.       return(TRUE);
  1119.     } else
  1120.       return(FALSE);
  1121.       }
  1122.  
  1123.       /* nasty 2D slanted line in an xz plane */
  1124.  
  1125.       if(!inrange(oy, y_min3d, y_max3d))
  1126.     return(FALSE);
  1127.  
  1128.       t[0] = (x_min3d - ix)/(ox - ix);
  1129.       t[1] = (x_max3d - ix)/(ox - ix);
  1130.       
  1131.       if(t[0] > t[1]) {
  1132.     swap = t[0];t[0] = t[1];t[1] = swap;
  1133.       }
  1134.  
  1135.       t[2] = (min3d_z - iz)/(oz - iz);
  1136.       t[3] = (max3d_z - iz)/(oz - iz);
  1137.       
  1138.       if(t[2] > t[3]) {
  1139.     swap = t[2];t[2] = t[3];t[3] = swap;
  1140.       }
  1141.  
  1142.       t_min = GPMAX(GPMAX(t[0],t[2]),0.0);
  1143.       t_max = GPMIN(GPMIN(t[1],t[3]),1.0);
  1144.       
  1145.       if(t_min > t_max)
  1146.     return(FALSE);
  1147.  
  1148.       lx[0] = ix + t_min * (ox - ix);
  1149.       ly[0] = iy;
  1150.       lz[0] = iz + t_min * (oz - iz);
  1151.       
  1152.       lx[1] = ix + t_max * (ox - ix);
  1153.       ly[1] = iy;
  1154.       lz[1] = iz + t_max * (oz - iz);
  1155.       
  1156.       /*
  1157.        * Can only have 0 or 2 intersection points -- only need test one coord
  1158.        */
  1159.       if(inrange(lx[0], x_min3d, x_max3d) && 
  1160.      inrange(lz[0], min3d_z, max3d_z))
  1161.     {
  1162.       return(TRUE);
  1163.     }
  1164.       
  1165.       return(FALSE);
  1166.     }
  1167.     
  1168.     if(iz == oz) {
  1169.       /* already checked cases (ix == ox && iz == oz) and (iy == oy && iz == oz) */
  1170.  
  1171.       /* nasty 2D slanted line in an xy plane */
  1172.  
  1173.       if(!inrange(oz, min3d_z, max3d_z))
  1174.     return(FALSE);
  1175.  
  1176.       t[0] = (x_min3d - ix)/(ox - ix);
  1177.       t[1] = (x_max3d - ix)/(ox - ix);
  1178.       
  1179.       if(t[0] > t[1]) {
  1180.     swap = t[0];t[0] = t[1];t[1] = swap;
  1181.       }
  1182.  
  1183.       t[2] = (y_min3d - iy)/(oy - iy);
  1184.       t[3] = (y_max3d - iy)/(oy - iy);
  1185.       
  1186.       if(t[2] > t[3]) {
  1187.     swap = t[2];t[2] = t[3];t[3] = swap;
  1188.       }
  1189.  
  1190.       t_min = GPMAX(GPMAX(t[0],t[2]),0.0);
  1191.       t_max = GPMIN(GPMIN(t[1],t[3]),1.0);
  1192.       
  1193.       if(t_min > t_max)
  1194.     return(FALSE);
  1195.  
  1196.       lx[0] = ix + t_min * (ox - ix);
  1197.       ly[0] = iy + t_min * (oy - iy);
  1198.       lz[0] = iz;
  1199.       
  1200.       lx[1] = ix + t_max * (ox - ix);
  1201.       ly[1] = iy + t_max * (oy - iy);
  1202.       lz[1] = iz;
  1203.       
  1204.       /*
  1205.        * Can only have 0 or 2 intersection points -- only need test one coord
  1206.        */
  1207.       if(inrange(lx[0], x_min3d, x_max3d) && 
  1208.      inrange(ly[0], y_min3d, y_max3d))
  1209.     {
  1210.       return(TRUE);
  1211.     }
  1212.       
  1213.       return(FALSE);
  1214.     }
  1215.   
  1216.     /* really nasty general slanted 3D case */
  1217.  
  1218.     /*
  1219.       Solve parametric equation
  1220.  
  1221.       (ix, iy, iz) + t (diff_x, diff_y, diff_z)
  1222.  
  1223.       where 0.0 <= t <= 1.0 and
  1224.  
  1225.       diff_x = (ox - ix);
  1226.       diff_y = (oy - iy);
  1227.       diff_z = (oz - iz);
  1228.      */
  1229.  
  1230.     t[0] = (x_min3d - ix)/(ox - ix);
  1231.     t[1] = (x_max3d - ix)/(ox - ix);
  1232.  
  1233.     if(t[0] > t[1]) {
  1234.       swap = t[0];t[0] = t[1];t[1] = swap;
  1235.     }
  1236.  
  1237.     t[2] = (y_min3d - iy)/(oy - iy);
  1238.     t[3] = (y_max3d - iy)/(oy - iy);
  1239.     
  1240.     if(t[2] > t[3]) {
  1241.       swap = t[2];t[2] = t[3];t[3] = swap;
  1242.     }
  1243.     
  1244.     t[4] = (iz == oz) ? 0.0 : (min3d_z - iz)/(oz - iz);
  1245.     t[5] = (iz == oz) ? 1.0 : (max3d_z - iz)/(oz - iz);
  1246.     
  1247.     if(t[4] > t[5]) {
  1248.       swap = t[4];t[4] = t[5];t[5] = swap;
  1249.     }
  1250.  
  1251.     t_min = GPMAX(GPMAX(t[0],t[2]),GPMAX(t[4],0.0));
  1252.     t_max = GPMIN(GPMIN(t[1],t[3]),GPMIN(t[5],1.0));
  1253.  
  1254.     if(t_min > t_max)
  1255.       return(FALSE);
  1256.  
  1257.     lx[0] = ix + t_min * (ox - ix);
  1258.     ly[0] = iy + t_min * (oy - iy);
  1259.     lz[0] = iz + t_min * (oz - iz);
  1260.  
  1261.     lx[1] = ix + t_max * (ox - ix);
  1262.     ly[1] = iy + t_max * (oy - iy);
  1263.     lz[1] = iz + t_max * (oz - iz);
  1264.  
  1265.     /*
  1266.      * Can only have 0 or 2 intersection points -- only need test one coord
  1267.      */
  1268.     if(inrange(lx[0], x_min3d, x_max3d) && 
  1269.        inrange(ly[0], y_min3d, y_max3d) &&
  1270.        inrange(lz[0], min3d_z, max3d_z))
  1271.       {
  1272.     return(TRUE);
  1273.       }
  1274.     
  1275.     return(FALSE);
  1276.   }
  1277.  
  1278.  
  1279.  
  1280.